(ns user.newMenu 
	(:import (javax.swing JFrame JPanel JButton JLabel table.DefaultTableModel JToolBar JTable JScrollPane JMenuBar JMenu JMenuItem JComboBox
		 table.DefaultTableModel JTextField SpringLayout KeyStroke event.TableModelListener event.TableModelEvent table.AbstractTableModel 
		 	table.TableRowSorter ImageIcon)
        (java.awt.event ActionListener ActionEvent KeyEvent)
        (java.awt BorderLayout Dimension Container FlowLayout GridLayout))
    (:use server.dbmain user.commonFunctions)
    (:use clojure.contrib.gen-html-docs)
)

;;------------------------------------------------------------------------------------------------------------------------------------

(defn create-menu-bar
	"Creates the menu bar for this application, receiving a each of the handlers
	that the components will use and return it.
	create-handler -- Indicates what happened when you press the create option
	find-handler -- Indicates what happened when you press the find option
	delete-handler -- Indicates what happened when you press the delete option
	exit-handler -- Indicates what happened when you press the exit option
	reload-handler -- Indicates what happened when you press the reload option"

	[create-handler find-handler delete-handler exit-handler reload-handler]
	(let [
		menuBar 	 (JMenuBar. )
        file    	 (JMenu. "File")
        help    	 (JMenu. "Help")
        create-item  (JMenuItem. "New")
        find-item	 (JMenuItem. "Find")
        delete-item	 (JMenuItem. "Delete")
        reload-item  (JMenuItem. "Reload")
        exit-item	 (JMenuItem. "Exit")
		]
		
		(.addActionListener create-item create-handler)
		(.addActionListener find-item find-handler)
		(.addActionListener delete-item delete-handler)
		(.addActionListener exit-item exit-handler)
		(.addActionListener reload-item reload-handler)
		
		(.setAccelerator create-item (KeyStroke/getKeyStroke KeyEvent/VK_N ActionEvent/CTRL_MASK))
		(.setAccelerator find-item   (KeyStroke/getKeyStroke KeyEvent/VK_F ActionEvent/CTRL_MASK))
		(.setAccelerator delete-item (KeyStroke/getKeyStroke KeyEvent/VK_D ActionEvent/CTRL_MASK))
		(.setAccelerator reload-item (KeyStroke/getKeyStroke KeyEvent/VK_R ActionEvent/CTRL_MASK))
	    ;(.setAccelerator exit-item   (KeyStroke/getKeyStroke KeyEvent/VK_X ActionEvent/ALT_MASK))
		
	    (doto file 
	    	(.add create-item)
	    	(.add find-item)
	    	(.add delete-item)
	    	(.add reload-item)
	    	(.addSeparator)
	    	(.add exit-item)
		)
		
		(.add menuBar file)
		(.add menuBar help)
		
		menuBar
	)
)

;;------------------------------------------------------------------------------------------------------------------------------------

(defn create-tool-bar
	"Creates the tool bar for this application, receiving each of the handlers
	that the components will use.
	create-handler -- Indicates what happened when you press the create button
	find-handler -- Indicates what happened when you press the find button
	delete-handler -- Indicates what happened when you press the delete button
	fields-combo-box -- Indicates the options to make a search
	find-text-field -- Indicates the word you are looking for
	reload-handler -- Indicates what happened when you press the reload button"

	[create-handler find-handler delete-handler fields-combo-box find-text-field reload-handler]
	(let [
		tool-bar 		(JToolBar. "Utilities")
		tool-bar-panel	(JPanel. (FlowLayout. FlowLayout/CENTER))
		create-icon		(ImageIcon. "new.gif")
		create-button 	(JButton. "" create-icon)
		find-icon		(ImageIcon. "find.gif")
		find-button		(JButton. "" find-icon)
		delete-icon		(ImageIcon. "delete.gif")
		delete-button	(JButton. "" delete-icon)
		reload-icon		(ImageIcon. "reload.gif")
		reload-button	(JButton. "" reload-icon)
		]
		
		(.setBorderPainted tool-bar true)
		(.setOrientation tool-bar JToolBar/HORIZONTAL)
		
		(doto tool-bar-panel
			(.add create-button)
			(.add delete-button)
			(.add fields-combo-box)
			(.add find-text-field)
			(.add find-button)
			(.add reload-button)
		)
		
		(doto tool-bar
			;(.addSeparator)
			(.add tool-bar-panel)
			;(.addSeparator)
		)
		
		(.setToolTipText create-button "add a new record")
		(.setToolTipText find-button "find in the database")
		(.setToolTipText fields-combo-box "select the field to find records")
		(.setToolTipText find-text-field "specify the criteria to find records")
		(.setToolTipText delete-button "delete the selected record")
		(.setToolTipText reload-button "reload all the records of the database")
		
		(.addActionListener create-button create-handler)
		(.addActionListener find-button find-handler)
		(.addActionListener delete-button delete-handler)
		(.addActionListener reload-button reload-handler)
		
		tool-bar
	)
)

;;------------------------------------------------------------------------------------------------------------------------------------

(defn get-current-rec-nos
	"Returns the rec-numbers of the rows already present in the table
	model -- the tabel model where rec-numbers will be looked-for"
	[model]
	(loop [
		rows		(.getRowCount model)
		index-row	0
		rec-nos 	'()
		]
		(if (< index-row rows)
			(recur rows (inc index-row) (conj rec-nos (.getValueAt model index-row 7)))
			rec-nos
		)
	)
)
;;------------------------------------------------------------------------------------------------------------------------------------

(defn filter-table
	"Filters only the values present in the table
	 data -- the dictionary with which the values will be filtered
	 model -- the table model where the filter will be performed"
	[data model dbname]
	(let [
		found-in-db		(find-in-db data dbname)
		found-in-table  (get-current-rec-nos model)
		in-db-and-table (for [x found-in-db y found-in-table :when (= x y)] x)
		dictionary		(map #(read-record dbname %) in-db-and-table)
		]
		(dorun (map #(.removeRow model %) (reverse (range (.getRowCount model)))))
		(dorun (map #(addRow-table %1 %2 model) in-db-and-table dictionary))
	)
)

;;------------------------------------------------------------------------------------------------------------------------------------

(defn filter-db
	"Shows the filtered values from the database in the current table.
	 data -- the dictionary with which the values will be filtered	
	 model -- the table model where the filter will be performed"
	[data model dbname]
	(let [
		found 		(find-in-db data dbname)
		dictionary	(map #(read-record dbname %) found)
		]
		(dorun (map #(.removeRow model %) (reverse (range (.getRowCount model)))))
		(dorun (map #(addRow-table %1 %2 model) found dictionary))
	)
)

;;------------------------------------------------------------------------------------------------------------------------------------

(defn table-model
	"Returns the table model of this interace's JTable"
	[]
	(let [class (atom (.getClass ""))
		model (proxy [DefaultTableModel] []
        (getColumnClass [column-number]
            (if (== column-number 0) (reset! class (.getClass "")))
            (if (== column-number 1) (reset! class (.getClass "")))
            (if (== column-number 2) (reset! class (.getClass (Integer. 0))))
            (if (== column-number 3) (reset! class (.getClass true)))
            (if (== column-number 4) (reset! class (.getClass "")))
            (if (== column-number 5) (reset! class (.getClass "")))
            (if (== column-number 6) (reset! class (.getClass (Integer. 0))))
            (if (== column-number 7) (reset! class (.getClass (Integer. 0))))
            @class
        ))]
        model
    )
)
  
;;------------------------------------------------------------------------------------------------------------------------------------

(defn row-for-rec
	"Returns the row number that has the corresponding rec-no.
	-1 if not found
	rec-no -- the record number to be looked for
	model -- the model table where the rec-no will be searched"
	[rec-no model]
	(loop [
		rows		(.getRowCount model)
		index-row	0
		]
		(if (< index-row rows)
			(if (= (.getValueAt model index-row 7) rec-no)
				index-row
				(recur rows (inc index-row))
			)
			(- 1)
		)
	)
)

;;------------------------------------------------------------------------------------------------------------------------------------

(defn new-menu
	"Displays the main menu"
	[dbname]
	(let [
		data 				{:name (str) :location (str) :rate (str) 
    	  	  				 :date (str) :size (str) :smoking (str) :owner (str)}
		frame   			(JFrame. "Main Menu")
		content-pane		(.getContentPane frame)
    	panel-table			(JPanel. )
    	panel-menu			(JPanel. )
    	model   			(table-model)
    	table   			(JTable. model)
    	scrollPane 			(JScrollPane. table)
    	return-list			(find-in-db data dbname)
    	dictionary			(map #(read-record dbname %) return-list)
    	margin 				5;
    	fields-combo-box 	(JComboBox. (into-array ["Name" "Location" "Size"
    							"Smoking" "Rate" "Date" "Owner"]))
        find-text-field		(JTextField. 15)
        sorter				(TableRowSorter. model)
    	
    	create-handler (proxy [ActionListener] []
        (actionPerformed [event]
        	(let [
        		new-rec-number (create-record 
        			dbname 
        			{:name "" :location "" :rate "$00.00" 
    	  	  		 :date "2010/01/01" :size "0" :smoking "N" :owner ""})
        		]
        		(addRow-table
        			new-rec-number
        			(assoc data :deleted "false") 		
    	  	  		model)
    	  	  	(.setModel table model)
    	  	  	(filter-db data model dbname)
    	  	  	(.setRowSelectionInterval table 0 0)
    	  	  	(let [row (row-for-rec new-rec-number model)]
    	  	  		(.setRowSelectionInterval table row row)
    	  	  	)
        	)
   	     ))
        find-handler (proxy [ActionListener] []
        (actionPerformed [event]
        	(let [
        		selected (.getSelectedIndex fields-combo-box)
        		find-field (if (not (empty? (.trim (.getText find-text-field)))) 
        				(upperLetter (.getText find-text-field)) (.getText find-text-field))
        		]
        		(if (= selected 0) (filter-table (assoc data :name 	 	find-field) model dbname))
        		(if (= selected 1) (filter-table (assoc data :location  find-field) model dbname))
        		(if (= selected 2) (filter-table (assoc data :size 	 	find-field) model dbname))
        		(if (= selected 3) (filter-table (assoc data :smoking  	find-field) model dbname))
        		(if (= selected 4) (filter-table (assoc data :rate 	 	find-field) model dbname))
        		(if (= selected 5) (filter-table (assoc data :date 	 	find-field) model dbname))
        		(if (= selected 6) (filter-table (assoc data :owner 	find-field) model dbname))
        	)
        ))
    	delete-handler (proxy [ActionListener] []
          	  (actionPerformed [event]
          	  	  (loop [n 0]
          	  	  	  (if (< n (.getRowCount table))
          	  	  	  	  (if (.isRowSelected table n)
          	  	  	  	  	  ;though we can't see the recNo in the table, the model keeps that data
          	  	  	  	  	  (do
          	  	  	  	  	  	  (delete-record dbname (.getValueAt model n 7))
          	  	  	  	  	  	  (.removeRow model n)
          	  	  	  	  	  )
          	  	  	  	  	  (recur (inc n))
          	  	  	  	  )
          	  	  	  )
          	  	  )
        ))
        update-handler (proxy [TableModelListener] []
          	  (tableChanged [event]
          	  	  (if (= TableModelEvent/UPDATE (.getType event))
					  (let [
						  row 	(.getFirstRow event)
						  nme  	(upperLetter (.getValueAt table row 0))
						  loc   (upperLetter (.getValueAt table row 1))
						  sze 	(.getValueAt table row 2)
						  smk 	(.getValueAt table row 3)
						  frate	(.getValueAt table row 4)
						  date 	(.getValueAt table row 5)
						  own 	(upperLetter (.getValueAt table row 6))
						  ]
						  
						  (update-record 
							  dbname 
							  (.getValueAt model row 7)
							  {:name nme :location loc :rate frate :owner (str own)
							  :date date :size (str sze) :smoking (if smk "Y" "N")}
						  )
						  (filter-db data model dbname)
					  )
				  )
        ))
        exit-handler (proxy [ActionListener] []
          	(actionPerformed [event]
          	(.setVisible frame false)
          	(.dispose frame)
        ))
        reload-handler (proxy [ActionListener] []
          	(actionPerformed [event]
          	(filter-db data model dbname)
        ))]
        
        
        (.setRowSorter table sorter)
        (.setDefaultCloseOperation frame JFrame/DISPOSE_ON_CLOSE)
		(.setPreferredSize frame (Dimension. 600 300))
		(.setLayout frame (BorderLayout.))
		(.setLayout content-pane (BorderLayout.))
		(.setLayout panel-menu (BorderLayout.))
		
		(.add panel-menu 
			(create-menu-bar create-handler find-handler delete-handler exit-handler reload-handler)
				BorderLayout/NORTH)
		(.add panel-menu 
			(create-tool-bar create-handler find-handler delete-handler fields-combo-box find-text-field reload-handler)
				BorderLayout/SOUTH)
				
		(.add content-pane panel-menu  BorderLayout/NORTH)
		
		(doto model
			(.addColumn "Name")
			(.addColumn "Location")
			(.addColumn "Size")
			(.addColumn "Smoking")
			(.addColumn "Rate")
			(.addColumn "Date")
			(.addColumn "Owner")
			(.addColumn "RecNo") 
		)
		
		(.setAutoResizeMode table JTable/AUTO_RESIZE_SUBSEQUENT_COLUMNS)
		(.setFillsViewportHeight table true)
		
		(.add content-pane scrollPane BorderLayout/CENTER)
		(.pack frame)
		(.setLocationRelativeTo frame nil)
		(.setVisible frame true)
		(dorun (map #(addRow-table %1 %2 model) return-list dictionary))
		
		(.setPreferredWidth (.getColumn (.getColumnModel table) 0) (+ 200 (* 2 margin))) ;name
		(.setPreferredWidth (.getColumn (.getColumnModel table) 1) (+ 150 (* 2 margin))) ;locatio
		(.setPreferredWidth (.getColumn (.getColumnModel table) 2) (+ 50 (* 2 margin)))  ;size
		(.setPreferredWidth (.getColumn (.getColumnModel table) 3) (+ 80 (* 2 margin)))	 ;smoking
		(.setPreferredWidth (.getColumn (.getColumnModel table) 4) (+ 100 (* 2 margin))) ;rate
		(.setPreferredWidth (.getColumn (.getColumnModel table) 5) (+ 100 (* 2 margin))) ;date
		(.setPreferredWidth (.getColumn (.getColumnModel table) 6) (+ 100 (* 2 margin))) ;owner
		
		(.addTableModelListener model update-handler)
		(.removeColumn table (.getColumn table "RecNo"))
	frame)
)

;;------------------------------------------------------------------------------------------------------------------------------------	

;(new-menu "db-1x2.db")
(generate-documentation-to-file "./user/documentation.html" ['user.newMenu 'user.commonFunctions 'server.readbinfile 'server.dbmain 'server.servidor 'user.cliente 'start])
